/*
 * FaceCat图形通讯框架
 * 著作权编号:2015SR229355+2020SR0266727
 * 上海卷卷猫信息技术有限公司
 */

#include "stdafx.h"
#include "FCServerSocket.h"

namespace FaceCat{
    SOCKDATA::SOCKDATA(){
        m_buffer = 0;
        m_bufferRemain = 0;
        m_get = false;
        m_head = 0;
        m_headSize = 4;
        memset(m_headStr, '\0', 4);
        m_index = 0;
        m_len = 0;
        m_pos = 0;
        m_socket = 0;
        m_str = 0;
        m_strRemain = 0;
        m_submit = false;
        m_readHead = 0;
    }

    SOCKDATA::~SOCKDATA(){
        if(m_str){
            delete m_str;
            m_str = 0;
        }
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    FCServerSocket::FCServerSocket(){
        m_hSocket = 0;
        m_recvEvent = 0;
        m_writeLogEvent = 0;
        m_newSocket = 0;
        m_state = 0;
        m_clearInterval = -1;
        m_limitTimes = -1;
        m_lastClearTime = 0;
        m_isIPV6 = false;
    }

    FCServerSocket::~FCServerSocket(){
        m_recvEvent = 0;
        m_writeLogEvent = 0;
        m_hSocket = 0;
        m_state = 0;
    }

    int FCServerSocket::close(int socketID){
        int ret = 0;
        ::shutdown(socketID, SHUT_RDWR);
        ret = ::close(socketID);
        writeLog(socketID, (int)m_hSocket, 2, "socket exit");
        return ret;
    }

    int FCServerSocket::closeClient(int socketID){
        int ret = 0;
        ret = ::close(socketID);
        writeLog(socketID, (int)m_hSocket, 2, "client socket exit");
        return ret;
    }

    int FCServerSocket::send(int socketID, const char *str, int len){
        return (int)::send(socketID, str, len, 0);
    }

    int FCServerSocket::recv(SOCKDATA *data){
        if(!data->m_submit){
            if(data->m_len >= 4 && data->m_buffer[0] == m_token[0] && data->m_buffer[1] == m_token[1] && data->m_buffer[2] == m_token[2] && data->m_buffer[3] == m_token[3]){
                data->m_submit = true;
                if (data->m_len == 4)
                {
                    return 1;
                }
                else
                {
                    int newLen = data->m_len - 4;
                    char buffer[1024] = {0};
                    memcpy(buffer, data->m_buffer + 4, newLen);
                    data->m_buffer = buffer;
                    data->m_len = newLen;
                }
            }
            else{
                return -1;
            }
        }
        int intSize = sizeof(int);
        data->m_index = 0;
        while(data->m_index < data->m_len){
            int diffSize = 0;
            if(!data->m_get){
                diffSize = intSize - data->m_headSize;
                if(diffSize == 0){
                    memcpy(&data->m_head, data->m_buffer + data->m_index, intSize);
                }
                else{
                    for(int i = 0; i < diffSize; i++){
                        data->m_headStr[data->m_headSize + i] = data->m_buffer[i];
                    }
                    memcpy(&data->m_head, data->m_headStr, intSize);
                }
                if(data->m_str){
                    delete[] data->m_str;
                    data->m_str = 0;
                }
                data->m_str = new char[data->m_head];
                memset(data->m_str, '\0', data->m_head);
                if(diffSize > 0){
                    for(int i = 0; i < data->m_headSize; i++){
                        data->m_str[i] = data->m_headStr[i];
                    }
                    data->m_pos += data->m_headSize;
                    data->m_headSize = intSize;
                }
            }
            data->m_bufferRemain = data->m_len - data->m_index;
            data->m_strRemain = data->m_head - data->m_pos;
            data->m_get = data->m_strRemain > data->m_bufferRemain;
            int remain = FCTran::minValue(data->m_strRemain, data->m_bufferRemain);
            memcpy(data->m_str + data->m_pos, data->m_buffer + data->m_index, remain);
            data->m_pos += remain;
            data->m_index += remain;
            if(!data->m_get){
                if(m_recvEvent){
                    m_recvEvent((int)data->m_socket, (int)m_hSocket, data->m_str, data->m_head);
                    data->m_readHead = 2;
                }
                data->m_head = 0;
                data->m_pos = 0;
                if(data->m_len - data->m_index == 0 || data->m_len - data->m_index >= intSize){
                    data->m_headSize = intSize;
                }
                else{
                    data->m_headSize = data->m_bufferRemain - data->m_strRemain;
                    memcpy(data->m_headStr, data->m_buffer + data->m_index, data->m_headSize);
                    break;
                }
            }
        }
        return 1;
    }

void* startListenerTCP(void *lpParam){
    FCServerSocket *server = (FCServerSocket*)lpParam;
    int socketID = server->m_newSocket;
    int len = 0;
    SOCKDATA sockData;
    int nextSize = 4;
    while(1){
        char buffer[1024] = {0};
        len = (int)recv(socketID, buffer, nextSize, 0);
        if(len == 0 || len == -1){
            if(server->m_recvEvent){
                char rmsg[1] = {(char)3};
                server->m_recvEvent(socketID, socketID, rmsg, 1);
            }
            break;
        }
        sockData.m_socket = socketID;
        sockData.m_buffer = buffer;
        sockData.m_len = len;
        if(!sockData.m_submit){
            int state = server->recv(&sockData);
            if(state == -1){
                server->writeLog(socketID, server->m_hSocket, 3, "socket error");
                break;
            }
        }else{
            if(sockData.m_readHead == 0){
                sockData.m_readHead = 1;
                server->recv(&sockData);
                int headSize2 = 0;
                memcpy(&headSize2, buffer, sizeof(int));
                memset(buffer, 0, 1024);
                nextSize = (headSize2 < 1024 ? headSize2 : 1024);
            }else{
                server->recv(&sockData);
                if(sockData.m_readHead == 1){
                    memset(buffer, 0, 1024);
                    int dataRemain = sockData.m_head - sockData.m_pos;
                    nextSize = (dataRemain < 1024 ? dataRemain : 1024);
                }else if(sockData.m_readHead == 2){
                    sockData.m_readHead = 0;
                    memset(buffer, 0, 1024);
                    nextSize = 4;
                }
            }
        }
    }
    ::close(socketID);
    if(server->m_recvEvent){
        char rmsg[1] = {(char)2};
        server->m_recvEvent(socketID, server->m_hSocket, rmsg, 1);
    }
    server->writeLog(socketID, server->m_hSocket, 2, "socket exit");
    server = 0;
    return 0;
}

    void* startAccept(void *lpParam){
        FCServerSocket *server = (FCServerSocket*)lpParam;
        while(server->m_hSocket){
            lstring ip = "";
            int port = 0;
            if(server->m_isIPV6){
                struct sockaddr_in6 client_addr;
                socklen_t length = sizeof(client_addr);
                server->m_newSocket = ::accept(server->m_hSocket, (struct sockaddr*)&client_addr, &length);
                char ipv6_str[INET6_ADDRSTRLEN];
                inet_ntop(AF_INET6, &client_addr, ipv6_str, INET6_ADDRSTRLEN);
                ip = ipv6_str;
                port = ntohs(client_addr.sin6_port);
            }else{
                struct sockaddr_in client_addr;
                socklen_t length = sizeof(client_addr);
                server->m_newSocket = ::accept(server->m_hSocket, (struct sockaddr*)&client_addr, &length);
                ip = inet_ntoa(client_addr.sin_addr);
                port = ntohs(client_addr.sin_port);
            }
            if(server->m_newSocket == -1){
                ::usleep(1000 * 1000);
            }else{
                bool limit = false;
                if(server->m_limitTimes > 0){
                    time_t tn;
                    time(&tn);
                    struct tm* t = localtime(&tn);
                    double time = FCTran::getDateNum(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, 0);
                    server->m_ipCachesLock.lock();
                    if(time - server->m_lastClearTime > server->m_clearInterval){
                        server->m_lastClearTime = time;
                        server->m_ipCaches.clear();
                    }
                    int count = 0;
                    if(server->m_ipCaches.containsKey(ip)){
                        count = server->m_ipCaches.get(ip) + 1;
                    }else{
                        count = 1;
                    }
                    server->m_ipCaches.put(ip, count);
                    if(count > server->m_limitTimes){
                        limit = true;
                    }
                    server->m_ipCachesLock.unLock();
                }
                server->m_blackIPsLock.lock();
                if(server->m_blackIPs.containsKey(ip)){
                    limit = true;
                }
                server->m_blackIPsLock.unLock();
                if(limit){
                    close(server->m_newSocket);
                }else{
                    char szAccept[1024] = {0};
                    sprintf(szAccept, "accept:%s:%d", ip.c_str(), port);
                    server->writeLog(server->m_newSocket, (int)server->m_hSocket, 1, szAccept);
                    pthread_t invokeThread;
                    pthread_create(&invokeThread, 0, startListenerTCP, server);
                }
            }
        }
        server->m_state = 1;
        return 0;
    }

    static int m_lastSocket = -1;

    int FCServerSocket::startTCP(int port, bool ipv6){
        m_isIPV6 = ipv6;
        m_port = port;
        int listenq = 1024;
        if(ipv6){
            bzero(&m_servaddr6, sizeof(m_servaddr6));
            m_servaddr6.sin6_family = AF_INET6;
            m_servaddr6.sin6_port = htons (port);
            m_hSocket = socket(AF_INET6, SOCK_STREAM, 0);
            socklen_t stlen = sizeof(m_servaddr6);
            const struct sockaddr *addr = (struct sockaddr*)&m_servaddr6;
            if (bind(m_hSocket, addr, stlen) == -1)
            {
                perror("bind error");
                return -1;
            }
        }else{
            bzero(&m_servaddr, sizeof(m_servaddr));
            m_servaddr.sin_family = AF_INET;
            m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
            m_servaddr.sin_port = htons (port);
            m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
            socklen_t stlen = sizeof(m_servaddr);
            const struct sockaddr *addr = (struct sockaddr*)&m_servaddr;
            if (bind(m_hSocket, addr, stlen) == -1)
            {
                perror("bind error");
                return -1;
            }
        }
        if (m_hSocket == -1)
        {
            perror("can't create socket file");
            return -1;
        }
        if (listen(m_hSocket, listenq) == -1)
        {
            perror("listen error");
            return -1;
        }
        m_lastSocket = m_hSocket;
        pthread_t invokeThread;
        pthread_create(&invokeThread, 0, startAccept, this);
        return m_hSocket;
    }

    void FCServerSocket::writeLog(int socketID, int localSID, int state, const char *log){
        if(m_writeLogEvent){
            m_writeLogEvent(socketID, localSID, state, log);
        }
    }

    int FCServerSocket::getLastSocket(){
        return m_lastSocket;
    }

    void FCServerSocket::addBlackIP(lstring ip){
        m_blackIPsLock.lock();
        m_blackIPs.put(ip, 0);
        m_blackIPsLock.unLock();
    }
    
    void FCServerSocket::removeBlackIP(lstring ip){
        m_blackIPsLock.lock();
        if(m_blackIPs.containsKey(ip)){
            m_blackIPs.remove(ip);
        }
        m_blackIPsLock.unLock();
    }
}
